from os import mkdir, startfile
from os.path import normpath, exists
from shutil import move, rmtree
from subprocess import Popen, PIPE
from threading import Thread, Event
from time import sleep
from PySide6.QtWidgets import QHBoxLayout, QVBoxLayout, QTableWidget, QTableWidgetItem, QWidget, QLabel, QPushButton, \
    QAbstractItemView
from PySide6.QtCore import Qt, Slot, Signal, QSize
from PySide6.QtGui import QPixmap, QFont, QFontMetrics

from core.get_url import getParseObject, getSourceCode, getModsID, getGameID, getModIcon, getModName, isCollect, \
    getTitle
from core.setting import BASEPATH, BASEMODINSTALL

BASEMODINSTALL = normpath(BASEMODINSTALL)

with open('setting','r',encoding='utf-8') as data:
    lines = data.readlines()
    MODSAVEPATH = eval(lines[1].split('=')[1].replace('\n', ''))
    IMGLOADTHREADCOUNT = eval(lines[2].split('=')[1].replace('\n', ''))
    DOWNLOADTHREADCOUNT = eval(lines[3].split('=')[1].replace('\n', ''))

class MyThread(Thread):
    def __init__(self, target=None, name=None, args=(), kwargs=None, daemon=True):
        super().__init__(target=target, name=name, args=args, kwargs=kwargs, daemon=daemon)
        self.result = None

    def run(self):
        try:
            if self._target is not None:
                self.result = self._target(*self._args, **self._kwargs)
        finally:
            del self._target, self._args, self._kwargs

    def get_result(self):
        try:
            return self.result
        except:
            return None


class previewWidget(QWidget):
    closePreview = Signal()
    def __init__(self, url: str, parent=None):
        super().__init__(parent)
        self._isSelectAll = False

        self.back = QPushButton()
        self.revertButton = QPushButton()
        self.openPath = QPushButton()
        self.downloadButton = QPushButton()
        self.selectAllButton = QPushButton()
        self.preview = previewView(url)

        self.bind()
        self.initSetup()

    def bind(self):
        self.preview.selectChange.connect(self.selectChangeEvent)
        self.preview.setCellfinished.connect(lambda: self.revertButton.show())
        self.selectAllButton.clicked.connect(self.buttonClickEvent)
        self.openPath.clicked.connect(self.openDownloadPath)
        self.downloadButton.clicked.connect(self.downloadMods)
        self.revertButton.clicked.connect(self.revertEvent)
        self.back.clicked.connect(self.backEvent)

    def initSetup(self):
        self.setObjectName('PreviewPage')
        self.setAttribute(Qt.WA_StyledBackground, True)  # 支持qss设置背景
        self.setFixedSize(self.parent().size())

        vlayout = QVBoxLayout(self)

        hlayout = QHBoxLayout()
        vlayout.addLayout(hlayout)
        vlayout.addWidget(self.preview, alignment=Qt.AlignmentFlag.AlignHCenter)
        vlayout.setSpacing(0)
        vlayout.setContentsMargins(10, 0, 0, 15)

        self.back.setFixedSize(36, 36)
        self.revertButton.setFixedSize(36, 36)
        self.openPath.setFixedSize(36,36)
        self.downloadButton.setFixedSize(36, 36)
        self.selectAllButton.setFixedSize(36, 36)
        self.back.setIconSize(QSize(36, 36))
        self.revertButton.setIconSize(QSize(36, 36))
        self.openPath.setIconSize(QSize(36, 36))
        self.downloadButton.setIconSize(QSize(36, 36))
        self.selectAllButton.setIconSize(QSize(36, 36))
        self.back.setIcon(QPixmap('./img/left.png'))
        self.revertButton.setIcon(QPixmap('./img/revision.png'))
        self.openPath.setIcon(QPixmap('./img/open_36.png'))
        self.downloadButton.setIcon(QPixmap('./img/download.png'))
        self.selectAllButton.setIcon(QPixmap('./img/noselect.png'))

        self.revertButton.hide()
        self.openPath.hide()

        hlayout.addWidget(self.back, alignment=Qt.AlignmentFlag.AlignLeft)
        hlayout.addWidget(self.revertButton, alignment=Qt.AlignmentFlag.AlignLeft)
        hlayout.addStretch(1)
        hlayout.addWidget(self.openPath,alignment=Qt.AlignmentFlag.AlignRight)
        hlayout.addWidget(self.downloadButton, alignment=Qt.AlignmentFlag.AlignRight)
        hlayout.addWidget(self.selectAllButton, alignment=Qt.AlignmentFlag.AlignRight)
        hlayout.setSpacing(5)
        distance = 35
        hlayout.setContentsMargins(distance, 0, distance, 0)

    @Slot(int)
    def selectChangeEvent(self, stateNumber: int):
        if stateNumber == 0:
            self.selectAllButton.setIcon(QPixmap('./img/noselect.png'))
            self._isSelectAll = False
        elif stateNumber == 1:
            self.selectAllButton.setIcon(QPixmap('./img/select.png'))
            self._isSelectAll = False
        elif stateNumber == 2:
            self.selectAllButton.setIcon(QPixmap('./img/selectall.png'))
            self._isSelectAll = True

    def closeReptileThreadings(self):
        # 获取爬虫相关线程并关闭线程
        self.preview.threadingEvent.set()

    def backEvent(self):
        # 关闭爬虫和下载线程
        self.closeReptileThreadings()
        # 切换界面
        self.closePreview.emit()

    def revertEvent(self):
        # 重启爬虫线程
        self.preview.startGetSourceThreading()
        self.revertButton.hide()
        self.preview.initItem()

    def openDownloadPath(self):
        if MODSAVEPATH[0] == '/':
            gamedir = BASEPATH + normpath(MODSAVEPATH) + normpath(f'/{self.preview.gameID}')
        else:
            gamedir = normpath(MODSAVEPATH) + normpath(f'/{self.preview.gameID}')
        startfile(gamedir)

    def downloadMods(self):
        if not self.preview.isSelectID:
            return

        self.downloadButton.hide()

        popenCount = DOWNLOADTHREADCOUNT
        downloadMods = self.preview.isSelectID.copy()
        self.preview.isSelectID.clear()

        for index, id in enumerate(downloadMods):
            if index < popenCount:
                self.preview.ModsDict[id].downloadState(True)
            else:
                self.preview.ModsDict[id].downloadState(False)

        downloadingThreading = Thread(target=self.downloading,args=(downloadMods, popenCount,))
        downloadingThreading.start()

    def downloading(self, mods, popenCount):
        if len(mods) < popenCount:
            popenCount = len(mods)

        PopenDict = {}
        for index, id in enumerate(mods, start=1):
            self.preview.ModsDict[id].downloadTip.setText('下载中')
            currentPopen = Popen(
                f'../steamcmd/steamcmd +login anonymous +workshop_download_item {self.preview.gameID} {id} +quit',
                encoding='utf-8',
                stdin=PIPE,
                stdout=PIPE,
                creationflags=0x08000000
            )

            PopenDict[id] = currentPopen
            PopenFinished = None

            if index % popenCount == 0 or index == len(mods):
                # 优化处：下载无法动态添加下载事件
                while True:
                    if PopenFinished is not None:
                        PopenDict.pop(PopenFinished)
                        PopenFinished = None

                    if not PopenDict:
                        break

                    sleep(0.6)
                    for id, popen in PopenDict.items():
                        if popen.poll() == 0:
                            modPath = normpath(f'/{self.preview.gameID}/{id}')
                            if exists(BASEPATH + BASEMODINSTALL + modPath):
                                self.preview.ModsDict[id].downloadTip.setText('下载成功')
                                if MODSAVEPATH[0] == '/':
                                    gamedir = BASEPATH + normpath(MODSAVEPATH) + normpath(f'/{self.preview.gameID}')
                                else:
                                    gamedir = normpath(MODSAVEPATH) + normpath(f'/{self.preview.gameID}')
                                if not exists(gamedir):
                                    mkdir(gamedir)
                                if exists(gamedir+normpath(f'/{id}')):
                                    rmtree(gamedir+normpath(f'/{id}'))
                                move(BASEPATH + BASEMODINSTALL + modPath,gamedir,)
                            else:
                                self.preview.ModsDict[id].downloadTip.setText('下载失败')

                            PopenFinished = id

        self.downloadButton.show()
        self.openPath.show()

    def buttonClickEvent(self):
        self._isSelectAll = not self._isSelectAll
        if self._isSelectAll:
            self.selectAllButton.setIcon(QPixmap('./img/selectall.png'))
            self.preview.isSelectID = list(self.preview.allMods)
        else:
            self.selectAllButton.setIcon(QPixmap('./img/noselect.png'))
            self.preview.isSelectID.clear()
        self.preview.isAllSelect.emit(self._isSelectAll)


class previewView(QTableWidget):
    addCell = Signal(int, int, str, str, str)
    addCellCount = Signal(int)
    setCellfinished = Signal()
    selectChange = Signal(int)
    isAllSelect = Signal(bool)
    def __init__(self, url: str, parent=None):
        super().__init__(parent)
        self.url = url
        self.threadingEvent = None
        self.gameID = None
        self.allMods = None
        self.modCount = 0
        self.isCollect = False
        self.ModsDict = {}
        self.isSelectID = []

        self.initSetup()

    def initSetup(self):
        self.setFixedSize(601, 281)
        self.setCursor(QPixmap('./img/mouse3.png'))

        self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)

        self.horizontalHeader().setVisible(False)
        self.verticalHeader().setVisible(False)
        self.setAutoScroll(False)

        self.setColumnCount(1)
        self.setRowCount(1)
        # bug: pyside6 设置qss时 需要设置默认行高和列宽 否则无法设置单元格行高和列宽
        self.horizontalHeader().setDefaultSectionSize(600)
        self.verticalHeader().setDefaultSectionSize(280)

        initItem = QTableWidgetItem('加载中，请等待...')
        initItem.setFont(QFont('微软雅黑', 18))
        initItem.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
        self.setItem(0, 0, initItem)

        self.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)

        self.bind()
        self.threadingEvent = self.startGetSourceThreading()

    def bind(self):
        self.addCell.connect(self.setCellItem)
        self.addCellCount.connect(self.setCellCount)

    @Slot(int)
    def setCellCount(self, rows):
        self.clear()
        self.setColumnCount(2)
        self.setRowCount(rows)
        self.setColumnWidth(0, 300)
        self.setColumnWidth(1, 300)

        for row in range(rows + 1):
            self.setRowHeight(row, 140)

    @Slot(int, int, str, str, str)
    def setCellItem(self, row, col, name, img, id):
        item = modItem(name=name, imgPath=img, modID=id)
        self.ModsDict[id] = item
        item.addID.connect(self.addID)
        item.delID.connect(self.delID)
        self.isAllSelect.connect(item.buttonChangeEvent)
        self.setCellWidget(row, col, item)

    @Slot(str)
    def addID(self, id: str):
        self.isSelectID.append(id)
        if self.modCount == 1:
            self.selectChange.emit(2)
        elif len(self.isSelectID) == 1:
            self.selectChange.emit(1)
        elif len(self.isSelectID) == self.modCount:
            self.selectChange.emit(2)

    @Slot(str)
    def delID(self, id: str):
        self.isSelectID.remove(id)
        if len(self.isSelectID) == 0:
            self.selectChange.emit(0)
        else:
            self.selectChange.emit(1)

    def initItem(self):
        self.clear()
        self.setColumnCount(1)
        self.setRowCount(1)

        self.setColumnWidth(0,600)
        self.setRowHeight(0,280)

        initItem = QTableWidgetItem('加载中，请等待...')
        initItem.setFont(QFont('微软雅黑', 18))
        initItem.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
        self.setItem(0, 0, initItem)

    def startGetSourceThreading(self):
        event = Event()
        getCodeThreading = Thread(target=self.getSource, name='getCode', args=(self.url,event,))
        getCodeThreading.start()
        return event

    def getSource(self, url: str,event:Event):
        object = getParseObject(getSourceCode(url, is_verify=False))

        if event.is_set():
            return

        if object is None:
            self.item(0, 0).setText('数据获取失败!请重试!')
            self.setCellfinished.emit()
            getHistoryThreading = Thread(target=self.get_history,args=(url,))
            getHistoryThreading.start()
            return

        self.isCollect = isCollect(object)

        if event.is_set():
            return

        getHistoryThreading = Thread(target=self.get_history, args=(url,),kwargs={'object':object})
        getHistoryThreading.start()

        name = self.rq_threading(getModName, kwargs={"object": object, 'isCollectItem': self.isCollect})
        imgs = self.rq_threading(getModIcon, kwargs={"object": object, 'isCollectItem': self.isCollect,"event":event})
        gameID = self.rq_threading(getGameID, args=(object,))
        modID = self.rq_threading(getModsID, args=(object,))

        setCellThreading = Thread(target=self.set_cell, name='set_cell', args=(name, imgs, gameID, modID,),kwargs={'event':event})
        setCellThreading.start()

    def get_history(self,url,object=None):
        if object is not None:
            title = getTitle(object)
        else:
            title = '重命名'
        with open('../log/historyLog.txt','a',encoding='utf-8') as data:
            data.write(f'{title}:{url}\n')

    @classmethod
    def rq_threading(cls, func, args=(), kwargs=None):
        funcThreading = MyThread(target=func, name=f'{func.__name__}',args=args, kwargs=kwargs)
        funcThreading.start()
        return funcThreading

    @classmethod
    def get_rq_result(cls, **kwargs) -> dict|None:
        re = {}
        event = kwargs['event']
        del kwargs['event']
        while True:
            if event is not None and event.is_set():
                return None
            sleep(1.5)
            if len(re.keys()) == len(kwargs.keys()):
                break
            for key, value in kwargs.items():
                result = value.get_result()
                if result:
                    re[key] = result
        return re

    def set_cell(self, name, imgs, gameID, modID,event:Event=None):
        re = self.get_rq_result(name=name, imgs=imgs, gameID=gameID, modID=modID,event=event)

        if event is not None and event.is_set():
            return

        self.gameID = re['gameID']

        l = len(re['name'])
        self.modCount = l
        self.allMods = re['modID']
        self.addCellCount.emit(round(l / 2) if l != 1 else 1)

        row = 0
        for index in range(l):
            if event is not None and event.is_set():
                return

            currentName = re['name'][index]
            currentImg = re['imgs'][index]
            currentModID = re['modID'][index]

            if (index + 1) % 2 == 0:
                self.addCell.emit(row, 1, currentName, currentImg, currentModID)
                row += 1
            else:
                self.addCell.emit(row, 0, currentName, currentImg, currentModID)

            sleep(0.05)

        self.setCellfinished.emit()


class modItem(QWidget):
    addID = Signal(str)
    delID = Signal(str)

    def __init__(self, name: str, imgPath: str, modID: str, parent=None):
        super().__init__(parent)
        self._isSelect = False

        self.name = name
        self.imgPath = './imgCache/' + imgPath
        self.modID = modID

        self.img = QLabel()
        self.text = QLabel()
        self.selectAction = QPushButton()
        self.downloadTip = QLabel('等待中')

        self.bind()
        self.initSetup()

    def bind(self):
        self.selectAction.clicked.connect(self.buttonClickEvent)

    def initSetup(self):
        self.setFixedSize(300, 140)
        layout = QHBoxLayout(self)
        vlayout = QVBoxLayout(self)

        pixmap = QPixmap(self.imgPath).scaled(120,
                                              120,
                                              Qt.AspectRatioMode.IgnoreAspectRatio,
                                              Qt.TransformationMode.SmoothTransformation)
        self.img.setPixmap(pixmap)
        self.img.setAlignment(Qt.AlignmentFlag.AlignVCenter)
        self.img.setFixedSize(120, 120)

        self.text.setFont(QFont('微软雅黑', 13))
        self.text.setFixedSize(170, 45)
        self.text.setWordWrap(True)
        self.text.setContentsMargins(0, 0, 0, 0)
        self.text.setToolTip(self.name)
        self.text.setStyleSheet('color:white;')
        self.text.setAlignment(Qt.AlignmentFlag.AlignLeft)
        metrics = QFontMetrics(self.text.font())
        name = metrics.elidedText(self.name, Qt.TextElideMode.ElideRight, self.text.width() + 50)
        self.text.setText(name)
        self.selectAction.setIcon(QPixmap('./img/noselect.png'))
        self.selectAction.setIconSize(QSize(36, 36))

        self.downloadTip.setFont(QFont('微软雅黑', 15))

        layout.addWidget(self.img, alignment=Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeft)

        vlayout.addWidget(self.text, alignment=Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignLeft)
        vlayout.addWidget(self.selectAction, alignment=Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight)

        layout.addLayout(vlayout)

    @Slot(bool)
    def buttonChangeEvent(self, bl: bool):
        self._isSelect = bl
        if bl:
            self.selectAction.setIcon(QPixmap('./img/selectall.png'))
        else:
            self.selectAction.setIcon(QPixmap('./img/noselect.png'))

    def downloadState(self, bl: bool):
        if bl:
            self.downloadTip.setText('下载中')
            self.layout().replaceWidget(self.selectAction, self.downloadTip)
        else:
            self.layout().replaceWidget(self.selectAction, self.downloadTip)
        self.selectAction.hide()

    def buttonClickEvent(self):
        self._isSelect = not self._isSelect
        if self._isSelect:
            self.selectAction.setIcon(QPixmap('./img/selectall.png'))
            self.addID.emit(self.modID)
        else:
            self.selectAction.setIcon(QPixmap('./img/noselect.png'))
            self.delID.emit(self.modID)
